/**
 * 	 DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *   @author Colin Redmond
 *   Copyright (C) 2011 Colin Redmond
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package com.cred.industries.platform.business.objects;

import java.util.Calendar;
import java.util.TimeZone;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cred.industries.core.exceptions.ApplicationException;
import com.cred.industries.core.server.ServerConfig;
import com.cred.industries.core.server.ServerConfigEnum;
import com.cred.industries.core.transferObj.result.ReturnCodes;
import com.cred.industries.core.util.SessionKey;
import com.cred.industries.platform.business.security.Authorization;
import com.cred.industries.platform.defines.Role;
import com.cred.industries.platform.session.SessionData;

/**
 * All business rules relating to the Session
 * and all the data needed to modify the session in the DB
 */
public class SessionBO implements Authorization {
	
	private static final Logger logger = LoggerFactory.getLogger(SessionBO.class);
	
	private String mSessionId;
	private int mCustomerId;
	private DateTime mTimeIssued;
	private DateTime mLastRequest;
	
	/**
	 * default consructor
	 */
	public SessionBO() {

	}
	
	/**
	 * constructor that takes in all needed member variables
	 * @param sessionId id for the session
	 * @param customerId id for the customer that owns this session
	 * @param timeIssued time the session was originally opened
	 * @param lastRequest last time the session was accessed
	 */
	public SessionBO(String sessionId, int customerId, DateTime timeIssued, DateTime lastRequest) {
		
		mSessionId = sessionId;
		mCustomerId = customerId;
		mTimeIssued = timeIssued;
		mLastRequest = lastRequest;
	}
	
	public String getSessionId() { return mSessionId; }
	public int getCustomerId() { return mCustomerId; }
	public DateTime getTimeIssued() { return mTimeIssued; }
	public DateTime getLastRequest() { return mLastRequest; }
	public void setLastRequest(DateTime lastRequest) { this.mLastRequest = lastRequest; }
		
	/**
	 * Creates a random session key and gets the current time
	 * @param custId the customer Id for the session
	 * @throws ApplicationException if not valid
	 */
	public void createSession(int custId) {
		
		//creates a session with a new random key
		mCustomerId = custId;
		mSessionId = SessionKey.createSessionKey(64);
		mTimeIssued = new DateTime(Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTimeInMillis(), DateTimeZone.UTC);
		mLastRequest = mTimeIssued;
		
		if(!validate()) {
			throw new ApplicationException(ReturnCodes.UNKNOWN_ERROR, "failed to createSession " + mCustomerId + " session ID: " + mSessionId);
		}
	}
	
	/**
	 * Validates all business rules for a session
	 * Such as session timeout and make sure we have a valid customer
	 * @return true if it is a valid session
	 * @throws IllegalArgumentException if not valid arguments
	 * @throws ApplicationException
	 */
	public boolean validate() {
		//validate the business rules
		if(mCustomerId <= 0)	{
			logger.error("createSession called without a custId set");
			throw new IllegalArgumentException("createSession called without a custId set");
		}
		
		if(mSessionId.length() != 64) {
			logger.error("createSession called without a valid sessionID");
			throw new IllegalArgumentException("createSession called without a valid sessionID");
		}
		
		int sessionTimeout = 3600;	//default session time out in case there isn't one set in the DB.
		sessionTimeout = ServerConfig.getInstance().getValue(Integer.class, ServerConfigEnum.SESSION_TIMEOUT_SECONDS);
		
		//time that the session is valid till is the current time minus the session timeout
		DateTime currentTime = new DateTime(DateTimeZone.UTC);
		currentTime = currentTime.minus(sessionTimeout * 1000);
		if(!mLastRequest.isAfter(currentTime)) {
			return false;
		}
		
		return true;
	}
	
	/**
	 * Validate that this session belongs to the currently authenticated User
	 * @return true if this session belongs to the currently authenticated User
	 */
	public boolean validateOwnership() {
		//verify that the person making this call is the one who owns this session
		CustomerBO cust = SessionData.getSessionCustomer();
		return cust.getCustomerId() == this.mCustomerId; 
	}
	
	/**
	 * returns if the currently authenticated customer is authorized to modify
	 * this customer BO. 
	 * a CS, admin or yourself can make changes to this BO
	 * @return true if this is a CS, admin or yourself
	 */
	@Override
	public boolean authorizedToModify() {

		//a CS, admin or yourself can make changes to this BO
		CustomerBO authenticatedUser = SessionData.getSessionCustomer();
		CustomerRolesBO roles = authenticatedUser.getRoles();
		return authenticatedUser != null
				&& (roles.hasRole(Role.ADMIN)
				|| roles.hasRole(Role.CS)
				|| this.mCustomerId == authenticatedUser.getCustomerId());
	}
	
	/**
	 * Only the owner of this BO can access it
	 * @return true
	 */
	@Override
	public boolean authorizedToAccess() {
		
		//any one can read this BO
		return validateOwnership();
	}
	
	/**
	 * returns if the currently authenticated customer is a super user
	 * Ie a user with permissions to change anything related to this BO
	 * In this case a CS or admin admin
	 * @return true if this is a CS, or admin.
	 */
	@Override
	public boolean authorizedAsSuperuser() {
		
		//a CS, admin or yourself can make changes to this BO
		CustomerBO authenticatedUser = SessionData.getSessionCustomer();
		CustomerRolesBO roles = authenticatedUser.getRoles();
		return authenticatedUser != null
				&& (roles.hasRole(Role.ADMIN)
				|| roles.hasRole(Role.CS));
	}
	
}
